﻿#include "FITKNastranAdaptorPart.h"

#include <functional>
#include <QDebug>]

namespace Nastran
{
FITKNastranAdaptorPart::~FITKNastranAdaptorPart()
{
}

/** AdaptR function checks the file content cache and reads different elements based on the content
 * It returns false if the cache is empty
 * If the first line starts with "GRID", it calls readGRID
 * If it starts with "CTRIA3", it calls readCTRIA3
 * If "CQUAD4", it calls readCQUAD4
 * If "CHEXA", it calls readCHEXA
 * If "CTETRA", it calls readCTETRA
 * If none match, it returns false
 */
bool FITKNastranAdaptorPart::adaptR()
{
    if (_fileContentCache.isEmpty())
    {
        return false;
    }

    QMap<QString, std::function<bool()>> readers;
    readers["GRID"] = [this]() -> bool { return this->readGRID(); };
    readers["CTRIA3"] = [this]() -> bool { return this->readCTRIA3(); };
    readers["CQUAD4"] = [this]() -> bool { return this->readCQUAD4(); };
    readers["CHEXA"] = [this]() -> bool { return this->readCHEXA(); };
    readers["CTETRA"] = [this]() -> bool { return this->readCTETRA(); };
    readers["GRID*"] = [this]() -> bool { return this->readGRID(); };
    readers["CTRIA3*"] = [this]() -> bool { return this->readCTRIA3(); };
    readers["CQUAD4*"] = [this]() -> bool { return this->readCQUAD4(); };
    readers["CHEXA*"] = [this]() -> bool { return this->readCHEXA(); };
    readers["CTETRA*"] = [this]() -> bool { return this->readCTETRA(); };


    QString command = _fileContentCache.head().left(8).simplified();
    if (readers.contains(command)) 
    {
        return readers[command]();
    }

    return false;
}

// Set the case data for the FITKNastranAdaptorPart class
void FITKNastranAdaptorPart::setCaseData(AbaqusData::FITKDataCase *caseData)
{
    // Assign the provided case data pointer to the class's case data
    _caseData = caseData;
}

// Assigns the given Abaqus part to the mesh part of the FITKNastranAdaptorPart.
void FITKNastranAdaptorPart::setMeshPart(AbaqusData::FITKAbaqusPart *part)
{
    _meshPart = part;
}

// Set the file content cache for the FITKNastranAdaptorPart
void FITKNastranAdaptorPart::setFileContentCache(QQueue<QString> cache)
{
    // Assign the provided cache to the internal file content cache
    _fileContentCache = cache;
}

/**
 * Checks for necessary data before reading GRID data.
 * Returns false if mesh part, reader, or file content is missing.
 *
 * Initializes file entry ID and point coordinates.
 *
 * Checks if the first line starts with "GRID*".
 * If true, extracts EID and coordinates from the first and next line.
 * If false, extracts EID and coordinates from the current line.
 *
 * Adds the node to the mesh if all data is available.
 * Returns true after successfully adding the node.
 */

bool FITKNastranAdaptorPart::readGRID()
{
    if (nullptr == _meshPart || nullptr == getReader() || _fileContentCache.isEmpty())
    {
        return false;
    }
    if (nullptr == _meshPart->getMesh())
    {
        getReader()->consoleMessage(3, "nullptr error of Mesh");
        return false;
    }


    int fileEID = -1;
    double point[3] = {-1, -1, -1};
    QString line = _fileContentCache.head();

    if (line.startsWith("GRID*"))
    {
        if (_fileContentCache.size() < 2)
            return false;
        fileEID = line.mid(8, 8).toInt();
        point[0] = line.mid(8 * 5, 16).toDouble();
        point[1] = line.mid(8 * 7, 16).toDouble();
        // There must be a next line for GRID*
        line = _fileContentCache[1];
        point[2] = line.mid(8 * 1, 16).toDouble();
        // You'd better pray that the sequence numbers of the GRID in the BDF file start from 1 and are consecutive.
    }
    else
    {
        bool OK = false;
        QString text = line.mid(8,8);
        fileEID = text.toInt(&OK);
        text = line.mid(24);
        OK &= this->readCoordinateShort(point, text);
        if (!OK)
        {
            this-> getReader()->consoleMessage(3, QString("Error at line \"%1\"").arg(line));
            return false;
        }
    }

  
    _meshPart->getMesh()->addNode(fileEID, point[0], point[1], point[2]);

    return true;
}

bool FITKNastranAdaptorPart::readCoordinateShort(double* coor, const QString & text)
{
    if (coor == nullptr || text.size() != 24) 
        return false;

    for (int i = 0;i<3; ++i)
    {
        QString coorS = text.mid(i * 8, 8);
        //处理 -.0256544情况  -0.0256544
        coorS = coorS.replace("-.", "-0.");
        //处理2.56565-2情况 2.56565e-2
        coorS = coorS.replace("-", "e-");
        coorS = coorS.simplified();
        if (coorS.startsWith("e"))
            coorS = coorS.mid(1);
        bool ok = false;
        //是否转换成功判断
        coor[i] = coorS.toDouble(&ok);
        if (!ok)
            return false;
    }
    return true;

}

/** Function to read CTRIA3 data from file content cache
 * Checks for necessary data and reader/helper objects
 * Parses the file content cache line by line
 * Extracts element ID, property ID, and node list
 * Appends the node list with data from the next line if format indicator present
 * Handles different line formats based on the presence of the format indicator
 * Appends the element to the mesh if mesh object is not null
 * Inserts cells group info using helper object
 * Returns true if successful, false otherwise
 */
bool FITKNastranAdaptorPart::readCTRIA3()
{
    if (nullptr == _meshPart || nullptr == _caseData || nullptr == getReader() || nullptr == getHelper() ||
        _fileContentCache.isEmpty())
    {
        return false;
    }

    int fileEID = 0;
    int filePID = 0;
    QString line = _fileContentCache.head();
    QList<int> nodeList{};

    if (line.startsWith("CTRIA3*"))
    {
        if (_fileContentCache.size() < 2)
        {
            getReader()->consoleMessage(3, "error format for CTRIA3");
        }
        fileEID = line.mid(8 + 16 * 0, 16).toInt();
        filePID = line.mid(8 + 16 * 1, 16).toInt();
        nodeList.append(line.mid(8 + 16 * 2, 16).toInt());
        nodeList.append(line.mid(8 + 16 * 3, 16).toInt());

        nodeList.append(_fileContentCache[1].mid(8 + 16 * 0, 16).toInt());
    }
    else
    {
        fileEID = line.mid(8 * 1, 8).toInt();
        filePID = line.mid(8 * 2, 8).toInt();
        nodeList.append(line.mid(8 * 3, 8).toInt());
        nodeList.append(line.mid(8 * 4, 8).toInt());
        nodeList.append(line.mid(8 * 5, 8).toInt());
    }

    if (nullptr == _meshPart->getMesh())
    {
        getReader()->consoleMessage(3, "nullptr error for mesh");
    }
    else
    {
        _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::S3, fileEID, nodeList);
    }

    getHelper()->insertCellsGroupInfo(filePID, fileEID);

    return true;
}

/**
 * Checks for necessary data and file content; if missing, returns false.
 * Parses CQUAD4 data from the file content, extracting element ID, property ID, and node IDs.
 * Depending on the line format, it processes the data differently.
 * If the mesh is not available, logs an error; otherwise, adds the element to the mesh.
 * Inserts cells group information using the helper object.
 * Returns true after successful processing.
 */
bool FITKNastranAdaptorPart::readCQUAD4()
{
    if (nullptr == _meshPart || nullptr == _caseData || nullptr == getReader() || nullptr == getHelper() ||
        _fileContentCache.isEmpty())
    {
        return false;
    }

    int fileEID = 0;
    int filePID = 0;
    QList<int> nodeList{};
    QString line = _fileContentCache.head();

    if (line.startsWith("CQUAD4*"))
    {
        if (_fileContentCache.size() < 2)
        {
            return false;
        }

        fileEID = line.mid(8 + 16 * 0, 16).toInt();
        filePID = line.mid(8 + 16 * 1, 16).toInt();
        nodeList.append(line.mid(8 + 16 * 2, 16).toInt());
        nodeList.append(line.mid(8 + 16 * 3, 16).toInt());

        nodeList.append(_fileContentCache[1].mid(8 + 16 * 0, 16).toInt());
        nodeList.append(_fileContentCache[1].mid(8 + 16 * 1, 16).toInt());
    }
    else
    {
        fileEID = line.mid(8 * 1, 8).toInt();
        filePID = line.mid(8 * 2, 8).toInt();
        nodeList.append(line.mid(8 * 3, 8).toInt());
        nodeList.append(line.mid(8 * 4, 8).toInt());
        nodeList.append(line.mid(8 * 5, 8).toInt());
        nodeList.append(line.mid(8 * 6, 8).toInt());
    }

    if (nullptr == _meshPart->getMesh())
    {
        getReader()->consoleMessage(3, "nullptr error for mesh");
    }
    else
    {
        _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::S4, fileEID, nodeList);
    }

    getHelper()->insertCellsGroupInfo(filePID, fileEID);

    return true;
}

/**
 * Checks for necessary data and returns false if any is missing.
 * If the first line starts with "CHEXA*", it proceeds; otherwise,
 * it checks for a specific format and reads element and node data.
 * Based on the number of nodes, it appends either a C3D8 or C3D20
 * element to the mesh and inserts cell group information.
 * Returns true upon successful completion.
 */
bool FITKNastranAdaptorPart::readCHEXA()
{
    if (nullptr == _meshPart || nullptr == _caseData || nullptr == getReader() || nullptr == getHelper() ||
        _fileContentCache.isEmpty())
    {
        return false;
    }

    int fileEID = 0;
    int filePID = 0;
    QList<int> nodeList{};
    QString line = _fileContentCache.head();

    if (line.startsWith("CHEXA*"))
    {
    }
    else
    {
        if (_fileContentCache.size() < 2 || !(_fileContentCache[1].startsWith("+")))
        {
            getReader()->consoleMessage(2, "wrong CHEXA format.");
            return false;
        }
        fileEID = line.mid(8 * 1, 8).toInt();
        filePID = line.mid(8 * 2, 8).toInt();
        nodeList.append(line.mid(8 * 3, 8).toInt());
        nodeList.append(line.mid(8 * 4, 8).toInt());
        nodeList.append(line.mid(8 * 5, 8).toInt());
        nodeList.append(line.mid(8 * 6, 8).toInt());
        nodeList.append(line.mid(8 * 7, 8).toInt());
        nodeList.append(line.mid(8 * 8, 8).toInt());

        nodeList.append(_fileContentCache[1].mid(8 * 1, 8).toInt());
        nodeList.append(_fileContentCache[1].mid(8 * 2, 8).toInt());

        if (_fileContentCache.size() > 2 && _fileContentCache[2].startsWith("+"))
        {
            nodeList.append(_fileContentCache[1].mid(8 * 3, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 4, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 5, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 6, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 7, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 8, 8).toInt());

            nodeList.append(_fileContentCache[2].mid(8 * 1, 8).toInt());
            nodeList.append(_fileContentCache[2].mid(8 * 2, 8).toInt());
            nodeList.append(_fileContentCache[2].mid(8 * 3, 8).toInt());
            nodeList.append(_fileContentCache[2].mid(8 * 4, 8).toInt());
            nodeList.append(_fileContentCache[2].mid(8 * 5, 8).toInt());
            nodeList.append(_fileContentCache[2].mid(8 * 6, 8).toInt());
        }
    }

    if (nullptr == _meshPart->getMesh())
    {
        getReader()->consoleMessage(3, "nullptr error for mesh");
    }
    else
    {
        if (nodeList.size() == 8)
        {
            _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::C3D8, fileEID, nodeList);
        }
        else
        {
            _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::C3D20, fileEID, nodeList);
        }
    }

    getHelper()->insertCellsGroupInfo(filePID, fileEID);

    return true;
}

// Check if necessary data structures are initialized
bool FITKNastranAdaptorPart::readCTETRA()
{
    if (nullptr == _meshPart || nullptr == _caseData || nullptr == getReader() || nullptr == getHelper() ||
        _fileContentCache.isEmpty())
    {
        return false;
    }

    // Element ID from the file
    // Property ID from the file
    // List to hold node IDs
    // Get the first line from the cache
    int fileEID = 0;
    int filePID = 0;
    QList<int> nodeList{};
    QString line = _fileContentCache.head();

    // Check if the line starts with "CTETRA*"
    if (line.startsWith("CTETRA*"))
    {
        // TODO: Handle CTETRA* case
    }
    else
    {
        // Extract element and property IDs, and node IDs from the line
        fileEID = line.mid(8 * 1, 8).toInt();
        filePID = line.mid(8 * 2, 8).toInt();
        nodeList.append(line.mid(8 * 3, 8).toInt());
        nodeList.append(line.mid(8 * 4, 8).toInt());
        nodeList.append(line.mid(8 * 5, 8).toInt());
        nodeList.append(line.mid(8 * 6, 8).toInt());

        // Check if the next line starts with "+" indicating additional nodes
        if (_fileContentCache.size() > 1 && _fileContentCache[1].startsWith("+"))
        {
            nodeList.append(line.mid(8 * 7, 8).toInt());
            nodeList.append(line.mid(8 * 8, 8).toInt());

            // Extract additional node IDs from the next line
            nodeList.append(_fileContentCache[1].mid(8 * 1, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 2, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 3, 8).toInt());
            nodeList.append(_fileContentCache[1].mid(8 * 4, 8).toInt());
        }
    }

    // Check if the mesh is initialized
    if (nullptr == _meshPart->getMesh())
    {
        getReader()->consoleMessage(3, "nullptr error for mesh");
    }
    else
    {
        // Append the element to the mesh based on the number of nodes
        if (nodeList.size() == 4)
        {
            _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::C3D4, fileEID, nodeList);
        }
        else
        {
            _meshPart->getMesh()->appendElement(Interface::FITKAbaMeshPartVTK::C3D10, fileEID, nodeList);
        }
    }

    // Insert cells group information
    getHelper()->insertCellsGroupInfo(filePID, fileEID);

    return true;
}

} // namespace Nastran
